// -----------------------------------------------------------------------
//  <copyright file="ValidateCodeService.cs" company="LiuliuSoft">
//      Copyright (c) 2022-2024 DaprPlus. All rights reserved.
//  </copyright>
//  <site>https://www.dapr.plus</site>
//  <last-editor>郭明锋</last-editor>
//  <last-date>2024-05-08 13:05</last-date>
// -----------------------------------------------------------------------

using DaprPlus.Domain;
using DaprPlus.Identity.Properties;

using Microsoft.Extensions.Caching.Distributed;

using OSharp.Caching;
using OSharp.Extensions;


namespace DaprPlus.Identity.Domain.Services;

public class ValidateCodeService(IDistributedCache cache) : IValidateCodeService
{
    public async Task<ValidateCode> GenerateAsync(string userInfo, ValidateCodeType type, int seconds, CancellationToken token = default)
    {
        var key = GetCacheKey(userInfo, type);
        var code = await cache.GetAsync<ValidateCode>(key, () =>
        {
            var c = new ValidateCode()
            {
                Code = GenerateCode(),
                ExpireTime = DateTime.Now.AddSeconds(seconds),
                UserInfo = userInfo,
                Type = type
            };
            return Task.FromResult(c);
        }, seconds, token);
        return code;
    }

    public async Task<ApiResult> ValidateAsync(string userInfo, ValidateCodeType type, string code, int maxFailCount = 3, CancellationToken token = default)
    {
        var key = GetCacheKey(userInfo, type);
        var item = await cache.GetAsync<ValidateCode>(key, token);
        if (item == null)
        {
            return ApiResult.Error(ResIdentity.String_ValidateCode_Expired);
        }

        if (item.Code != code)
        {
            item.FailCount++;
            if (item.FailCount < maxFailCount)
            {
                var seconds = (int)item.ExpireTime.Subtract(DateTime.Now).TotalSeconds;
                await cache.SetAsync(key, item, seconds, token);
                return new ApiResult(ApiResultType.Error, ResIdentity.Format_ValidateCode_ValidFail_LeftTimes.FormatWith(maxFailCount - item.FailCount));
            }

            await cache.RemoveAsync(key, token);
            return ApiResult.Error(ResIdentity.Format_ValidateCode_ValidFail_Resend);
        }

        await cache.RemoveAsync(key, token);
        return ApiResult.Success(ResIdentity.Format_ValidateCode_ValidSuccess);
    }

    private string GetCacheKey(string userInfo, ValidateCodeType type)
    {
        return $"Common:ValidateCode:{type.ToDescription()}:{userInfo}";
    }

    protected virtual string GenerateCode(int length = 6)
    {
        var random = new Random();
        var code = "";
        for (var i = 0; i < length; i++)
        {
            int digit;
            do
            {
                digit = random.Next(0, 10);
            } while (digit == 4);
            code += digit.ToString();
        }
        return code;
    }
}
